﻿
using System.ComponentModel;
using System.Security.Cryptography;

namespace Timo.Common.Extension;

/// <summary>
/// 签名算法
/// </summary>
public static class SignHelper
{
    private static int signIntervalSecond { get; set; } = 7200;

    /// <summary>
    /// 常规签名算法
    /// 签名规则：
    /// 1、将所有请求参数按key1=value1&key2=value2拼接
    /// 2、将拼接后的数据按ascii码升序排序
    /// 3、将排序后的字符串拼接上"key=key值"，得到最终需要进行加密签名的字符串
    /// 4、对字符串进行md5加密转大写，得到最终签名结果
    /// 说明：请求参数必须添加 “nonce”随机数字段、“time_stamp”10位当前时间戳字段
    /// </summary>
    /// <param name="dic_param">需要签名的数据</param>
    /// <param name="key">签名key</param>
    /// <returns>签名结果字符串</returns>
    public static string Sign(Dictionary<string, string> dic_param, string key)
    {
        string signstr = string.Empty;
        Dictionary<string, string> dic = new Dictionary<string, string>();
        dic = dic_param;
        if (dic_param == null || dic_param.Count <= 0)
            return signstr;
        if (!dic_param.ContainsKey("time_stamp"))
            return signstr;
        //字典升序
        dic_param = dic_param.OrderBy(o => o.Key).ToDictionary(o => o.Key, p => p.Value);

        //拼接按URL键值对
        string str = string.Empty;
        foreach (KeyValuePair<string, string> item in dic_param)
        {
            if (!string.IsNullOrEmpty(item.Value))
            {
                if (item.Key.Equals("sign"))
                    continue;
                if (item.Key.Equals("key"))
                    continue;
                str += item.Key + "=" + item.Value + "&";
                //if (item.Key.Equals("nonce"))
                //{
                //    //注意nonce的值不能大写，否则sign签名值不对
                //    str += item.Key + "=" + System.Web.HttpUtility.UrlEncode(item.Value) + "&";
                //}
                //else
                //{
                //    //注意其它URL键值必须大写，否则sign签名值不对
                //    str += item.Key + "=" + System.Web.HttpUtility.UrlEncode(item.Value).ToUpper() + "&";
                //}
            }
        }

        //拼接key
        str = str + "key=" + key;
        // MD5运算，得到请求签名
        MD5 mD5 = MD5.Create();
        byte[] bytes = UTF8Encoding.UTF8.GetBytes(str);
        byte[] sign_byte = mD5.ComputeHash(bytes);
        foreach (byte item in sign_byte)
        {
            signstr += item.ToString("X2");
        }
        return signstr;
    }

    /// <summary>
    /// 签名验证
    /// </summary>
    /// <param name="dic_param">需要签名的数据</param>
    /// <param name="key">签名key</param>
    /// <param name="sign">需要被验证的签名字符串</param>
    /// <returns>签名结果。成功、验证失败、签名失效...</returns>
    public static SignStateEnum ValidateSign(Dictionary<string, string> dic_param, string key, string sign)
    {
        var res = SignStateEnum.fail;
        if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(sign))
            return res;
        var time_stamp = 0L;
        var sign_str = string.Empty;
        try
        {
            if (dic_param == null || dic_param.Count <= 0)
                return res;
            //校验签名是否过期
            if (!dic_param.ContainsKey("time_stamp"))
                return res;
            time_stamp = long.Parse(dic_param["time_stamp"].ToString());
            var now = TimeHelper.GetTimeStamp();
            if ((now - time_stamp) > signIntervalSecond)
                return res = SignStateEnum.expire;
            sign_str = Sign(dic_param, key);
            if (sign_str.Equals(sign))
                res = SignStateEnum.success;
        }
        catch (Exception ex)
        {
            res = SignStateEnum.fail;
        }
        return res;
    }
}

public enum SignStateEnum
{
    [Description("成功")]
    success = 1,
    [Description("已过期")]
    expire = 2,
    [Description("签名验证失败")]
    fail = 3
}